Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

27장. Database 작업 — Schema · Query · Migration · Index

DB 작업은 성격이 둘로 갈린다.

읽는 쪽은 되돌릴 수 있고,
쓰는 쪽은 되돌릴 수 없다.

에이전틱 코딩에서 이 구분이 그대로 경계선이 된다.


스키마를 파악시키는 방법

12장에서 스키마 덤프를 오염원으로 꼽았다.

테이블 200개짜리 모놀리스에서
\d+ 를 전부 넣으면 Context가 바로 찬다.

필요한 것만 넣는다.

주문 취소와 관련된 테이블 구조를 확인해줘.

- 엔티티 클래스에서 먼저 찾아줘 (order, payment, point 패키지)
- 실제 DDL이 필요하면 해당 테이블만 조회해줘

🔥 JPA를 쓴다면 엔티티가 스키마 문서다.

@Entity 클래스를 읽는 편이
DB에 붙는 것보다 싸고 정확하다.

다만 둘이 어긋나 있을 수 있다.
레거시에서는 흔하다.

OrderEntity 와 실제 orders 테이블의 컬럼이 일치하는지 확인해줘.
차이가 있으면 목록만 보여줘.

쿼리 — 잘 쓰지만 계획은 모른다

Agent는 쿼리를 잘 쓴다.

JPQL, QueryDSL, 네이티브 쿼리 모두
문법적으로 정확하게 만든다.

⚠️ 그런데 그 쿼리가 어떻게 실행되는지는 모른다.

인덱스가 있는지, 데이터가 몇 건인지,
카디널리티가 어떤지 코드에는 없다.

그래서 습관 하나가 필요하다.

이 쿼리의 실행 계획을 확인해줘.

EXPLAIN 결과를 보여주고,
풀스캔이 있으면 어떤 인덱스가 필요한지 알려줘.

로컬 DB에 운영과 비슷한 데이터가 없으면
실행 계획도 의미가 없다.

그 사실을 CLAUDE.md 에 적어둔다.

- 로컬 DB는 데이터가 적어 실행 계획이 운영과 다르다
  쿼리 성능 판단은 사람에게 확인을 요청한다

마이그레이션이 특별한 이유

DB 작업에서 유일하게 되돌릴 수 없는 부분이다.

flowchart LR
    A[마이그레이션 작성] -->|Agent| B[검토]
    B -->|사람| C[실행]
    C -->|사람| D[운영 반영]

Agent는 작성까지만 한다.

# CLAUDE.md
- 마이그레이션 파일 작성까지만 한다. 실행하지 않는다
- `flyway migrate`, `liquibase update` 를 실행하지 않는다
{
  "permissions": {
    "deny": [
      "Bash(./gradlew flywayMigrate:*)",
      "Bash(psql:*)"
    ]
  }
}

7장의 원칙 그대로다.

되돌릴 수 없는 일은 사람이 결정한다.


위험한 마이그레이션 패턴

Agent가 만든 마이그레이션에서 반드시 확인할 것들이다.

패턴무슨 일이 일어나는가
NOT NULL 컬럼 추가기존 행이 있으면 실패
컬럼 rename구버전 앱이 죽는다
컬럼 타입 변경테이블 잠금 + 데이터 손실
큰 테이블에 인덱스 추가잠금 시간이 길다
DROP COLUMN롤백 불가
기본값 있는 컬럼 추가DB 버전에 따라 전체 rewrite

⚠️ 두 번째와 세 번째가 배포 사고의 단골이다.

Agent는 최종 상태만 본다.
배포 중간에 구버전과 신버전이 함께 도는 시간을 고려하지 않는다.


확장-수축 패턴을 규칙으로

무중단 배포에서 쓰는 순서다.

이것을 CLAUDE.md 에 적어두면
Agent가 매번 맞게 만든다.

## 마이그레이션 규칙

컬럼 변경은 3단계로 나눈다 (배포 3회)

1. 확장: 새 컬럼 추가 (nullable, 기본값 없음)
2. 이행: 양쪽 쓰기 → 백필 → 읽기 전환
3. 수축: 구 컬럼 제거

- `NOT NULL` 은 백필 완료 후 별도 마이그레이션으로 건다
- 컬럼 rename은 하지 않는다 (추가 → 이행 → 제거)
- 100만 건 이상 테이블 인덱스는 온라인 방식으로 생성한다

🔥 이 열 줄이 마이그레이션 사고의 대부분을 막는다.

Agent가 몰라서 틀리는 것이지,
알려주면 정확히 지킨다.


인덱스 판단은 사람이 한다

인덱스는 코드만 봐서는 결정할 수 없다.

Agent가 아는 것알 수 없는 것
어떤 컬럼으로 조회하는가데이터 분포
쿼리 문법실제 카디널리티
기존 인덱스 목록쓰기 부하
이 인덱스가 이미 다른 것에 포함되는지

그래서 이렇게 나눈다.

인덱스가 필요해 보이는 쿼리를 찾아줘.

- 어떤 컬럼 조합이 필요한지 후보만 제시해줘
- 기존 인덱스로 커버되는지 확인해줘
- 마이그레이션은 아직 만들지 마

후보 도출은 Agent가, 결정은 사람이.


트랜잭션 경계는 다음 장에서

DB 작업에서 가장 자주 나는 사고는
사실 쿼리도 마이그레이션도 아니다.

트랜잭션 경계다.

28장에서 따로 다룬다.


완료 조건 예시

## Acceptance Criteria
- 마이그레이션 파일이 V32 번호로 생성됨 (실행하지 않음)
- 로컬에서 `flyway migrate` 를 사람이 실행해 성공 확인
- 롤백 스크립트 또는 되돌리는 방법이 문서화됨
- 신규 컬럼은 nullable, 기본값 없음
- 기존 24개 결제 테스트 통과

⚠️ 세 번째 항목을 자주 빠뜨린다.

되돌리는 방법이 없는 마이그레이션은
배포 후에 손이 묶인다.


이 장의 핵심

  • DB 작업은 읽는 쪽과 쓰는 쪽의 성격이 다르다
  • JPA 엔티티가 가장 싼 스키마 문서다 — 다만 실제 DDL과 어긋날 수 있다
  • Agent는 쿼리를 잘 쓰지만 실행 계획은 모른다
  • 로컬 데이터가 적으면 실행 계획도 신뢰할 수 없다 — 그 사실을 적어둔다
  • 마이그레이션은 작성까지만 맡기고 실행은 사람이 한다
  • Agent는 최종 상태만 보고 배포 중간 상태를 고려하지 않는다
  • 확장-수축 3단계를 규칙으로 적어두면 대부분의 사고가 막힌다
  • 인덱스 후보는 Agent가 뽑고 결정은 사람이 한다
  • 되돌리는 방법이 없는 마이그레이션은 배포 후 손을 묶는다